package com.ala4.oxcafe.service.impl;

import com.ala4.oxcafe.boot.page.PageDomain;
import com.ala4.oxcafe.boot.utils.DetailsHelper;
import com.ala4.oxcafe.common.utils.OssClientUtil;
import com.ala4.oxcafe.common.utils.RandomUtil;
import com.ala4.oxcafe.common.utils.collection.CollUtil;
import com.ala4.oxcafe.common.utils.date.DateUtil;
import com.ala4.oxcafe.common.utils.object.ObjectUtil;
import com.ala4.oxcafe.common.utils.text.StrUtil;
import com.ala4.oxcafe.entity.Attachment;
import com.ala4.oxcafe.enums.StorageTypeEnum;
import com.ala4.oxcafe.exception.BusinessException;
import com.ala4.oxcafe.properties.FileProperties;
import com.ala4.oxcafe.repository.AttachmentRepository;
import com.ala4.oxcafe.service.AttachmentService;
import com.aliyun.oss.dto.OSSUrlDTO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 系统附件存储服务
 *
 * @author PING
 * @date 2025/8/12 22:59
 */
@Slf4j
@Service
@AllArgsConstructor
public class AttachmentServiceImpl implements AttachmentService {

    private final AttachmentRepository attachmentRepository;

    private final FileProperties fileProperties;

    /**
     * 支持上传的文件后缀
     */
    private final List<String> fileSuffixList = CollUtil.newArrayList(".jpg", ".jpeg",
            ".png", ".gif", ".pdf", ".xlsx",
            ".xls", ".doc", ".docx", ".mp4", ".avi", ".wmv", ".flv");

    @Override
    public void createAttachment(StorageTypeEnum storageTypeEnum, String fileKey, String fileName, String storagePath) {


        int pointIndex = fileName.lastIndexOf(".");
        if (pointIndex < 0) {
            throw new BusinessException("文件名请带后缀");
        }
        String fileSuffix = fileName.substring(pointIndex + 1);

        Attachment attachment = new Attachment();
        attachment.setStorageType(storageTypeEnum);
        attachment.setFileKey(fileKey);
        attachment.setFileName(fileName);
        attachment.setFileType(fileSuffix.toUpperCase());
        attachment.setStoragePath(storagePath);
        attachment.setUploaderId(DetailsHelper.getUserDetails().getUserId());
        attachment.setUploadTime(LocalDateTime.now());
        attachmentRepository.save(attachment);
    }

    @Override
    public void checkFileSuffix(String fileName) {

        int pointIndex = fileName.lastIndexOf(".");
        if (pointIndex < 0) {
            throw new BusinessException("文件名请带后缀");
        }
        String fileSuffix = fileName.substring(pointIndex);

        // 判断后缀是否支持
        if (!fileSuffixList.contains(fileSuffix.toLowerCase())) {
            throw new BusinessException("不支持该文件上传,支持的文件后缀:" + fileSuffixList);
        }
    }

    @Override
    public OSSUrlDTO selectDownloadByFileKey(String fileKey) {
        // 获取附件对象
        Attachment attachment = selectByFileKey(fileKey);

        return this.downloadUrl(attachment);
    }

    @Override
    public OSSUrlDTO downloadUrl(Attachment attachment) {

        // 如果是OSS那么就从OSS拿下载地址
        if (StorageTypeEnum.OSS.equals(attachment.getStorageType())) {
            OSSUrlDTO ossUrlDTO = OssClientUtil.policyUrl(attachment.getStoragePath());
            ossUrlDTO.setFileKey(attachment.getFileKey());
            return ossUrlDTO;
        }
        if (StorageTypeEnum.LOCAL.equals(attachment.getStorageType())) {
            String url = fileProperties.getStaticHostPath() + attachment.getStoragePath();
            return new OSSUrlDTO(url, attachment.getFileKey(), "1000");
        }
        throw new BusinessException("附件错误,无法获取下载地址");
    }

    @Override
    public Attachment selectByFileKey(String fileKey) {
        if (StrUtil.isEmpty(fileKey)) {
            throw new BusinessException("文件key不能为空");
        }
        Attachment attachment = attachmentRepository.getOne(Wrappers.<Attachment>lambdaQuery().eq(Attachment::getFileKey, fileKey));
        if (ObjectUtil.isNull(attachment)) {
            throw new BusinessException("附件信息不存在");
        }
        return attachment;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Attachment uploadLocalAttachment(MultipartFile multipartFile, String fileKey) throws IOException {

        // 校验文件是否支持上传
        this.checkFileSuffix(Objects.requireNonNull(multipartFile.getOriginalFilename()));

        // 根据fileKety获取stor
        Attachment attachment = this.selectByFileKey(fileKey);
        attachment.setFileSize(Integer.valueOf(String.valueOf(multipartFile.getSize())));
        attachment.setUploadStatus(1);

        // 判断文件夹是否存在
        // 获取完整的文件路径（包含文件名）
        String fullFilePath = fileProperties.getUploadFolder() + attachment.getStoragePath();
        File file = new File(fullFilePath);

        // 获取文件所在的父目录（文件夹）
        File parentFolder = file.getParentFile();

        // 如果父目录不存在，则递归创建所有必需的目录
        if (parentFolder != null && !parentFolder.exists()) {
            boolean created = parentFolder.mkdirs();
            if (!created) {
                // 处理创建失败的情况（例如权限不足、磁盘空间不足等）
                throw new BusinessException("无法创建目录: " + parentFolder.getAbsolutePath());
            }
        }

        // 直接写入文件
        Files.write(new java.io.File(fullFilePath).toPath(), multipartFile.getBytes());

        // 更新文件大小
        attachmentRepository.updateById(attachment);

        return attachment;
    }

    @Override
    public Attachment updateAttachment(String fileKey, String size) {
        Attachment attachment = this.selectByFileKey(fileKey);
        attachment.setFileSize(Integer.valueOf(size));
        attachment.setUploadStatus(1);
        attachmentRepository.updateById(attachment);
        return attachment;
    }

    @Override
    public IPage<Attachment> attachmentPage(PageDomain pageDomain, Attachment attachment) {

        return attachmentRepository.selectAttachmentPage(pageDomain, attachment);
    }

    @Override
    public List<Attachment> attachmentList(Attachment attachment) {
        return attachmentRepository.selectAttachmentList(attachment);
    }

    @Override
    public void saveAttachmentList(List<Attachment> attachmentList) {
        List<String> fileKeyList = attachmentList.stream().map(Attachment::getFileKey).toList();
        // 根据filekey去保存业务信息
        List<Attachment> dbAttachmentList = attachmentRepository.list(Wrappers.<Attachment>lambdaQuery().in(Attachment::getFileKey, fileKeyList));
        Map<String, Attachment> dbAttachmentMap = dbAttachmentList.stream().collect(Collectors.toMap(Attachment::getFileKey, Function.identity()));
        for (Attachment attachment : attachmentList) {
            Attachment dbAttachment = dbAttachmentMap.get(attachment.getFileKey());
            if (ObjectUtil.isNotNull(dbAttachment)) {
                dbAttachment.setRelatedId(attachment.getRelatedId());
                dbAttachment.setRelatedModule(attachment.getRelatedModule());
            }
        }
        attachmentRepository.updateBatchById(dbAttachmentList);
    }

    @Override
    public String generateFileName(String fileName, StorageTypeEnum storageTypeEnum) {
        return storageTypeEnum.toString() + RandomUtil.randomString(10) + fileName;
    }

    @Override
    public String generateFilePath(String fileName) {
        return DateUtil.nowDateString("yyyy/MM/") + fileName;
    }

}
